#include "mystdio.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

//仅用于申请一个MY_FILE对象（static 函数只在本文件有效）
static MY_FILE* buy_file(int fd,int flag)
{
    MY_FILE* open_file = (MY_FILE*)malloc(sizeof(MY_FILE));
    if(open_file == NULL)
        return NULL;
    open_file->fileno = fd;
    open_file->bufferlen = 0;
    open_file->flag = flag;
    memset(open_file->outbuffer,0,sizeof(open_file->outbuffer));
    open_file->flush_method = LINE_FLUSH;//为了简易，默认行刷新     

    return open_file;
}

MY_FILE* my_fopen(const char* path,const char* mode)
{
    int fd = -1;
    int flag = 0;
    if(strcmp(mode,"r") == 0)//只读方式打开
    {
        flag = O_RDONLY;
        fd = open(path,flag,0666);
    }
    else if(strcmp(mode,"w") == 0)//写方式打开
    {
        flag = O_CREAT|O_TRUNC|O_WRONLY;
        fd = open(path,flag,0666);
    }
    else if (strcmp(mode,"a") == 0) //追加方式打开
    {
        flag = O_CREAT|O_APPEND|O_WRONLY;
        fd = open(path,flag,0666);
    }
    if (fd < 0)
        return NULL;
    return buy_file(fd,flag);
    //为了简易，默认不把文件内的内容拷贝至缓冲区，默认为空文件
}

//C语言层级的写入本质是拷贝到库级别的缓冲区中
size_t my_fwrite(const void*ptr, size_t size,size_t count,MY_FILE* stream)
{    
    size_t write_len = size*count;
    //为了简易，默认追加写入,实际上MY_FILE结构体内应当定义此时在缓冲区内部的偏移量
    memcpy(stream->outbuffer + stream->bufferlen,ptr,write_len);
    stream->bufferlen += write_len;
    
    //判断是否满足刷新条件
    
    if((stream->flush_method == LINE_FLUSH) && stream->outbuffer[stream->bufferlen - 1] == '\n')
    {
        my_fflush(stream);
    }
}

int my_fclose(MY_FILE* stream)
{
    if(stream->fileno < 0)
        return -1;
    my_fflush(stream);//关闭文件前会进行刷新，这也是为什么我们要记得关闭
    //最后调用系统调用，关闭文件描述符
    close(stream->fileno);
    free(stream);
    return 0;
}

int my_fflush(MY_FILE* stream)
{
    if(stream->bufferlen == 0)
    {
        return -1;
    }
    //默认把缓冲区内所有内容刷新到系统中-同步至系统缓冲区中
    int n = write(stream->fileno,stream->outbuffer,stream->bufferlen); 
    (void)n;
    stream->bufferlen = 0;
    return 0;
}
